<?php
###
#  Baing PHP 框架 1.0.0
#  http://baing.co/
#
#  Copyright: 白晓阳
#  License: CC BY-NC-SA 4.0
#  https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh
###

namespace bai\resource;

use bai\feature\RuntimeException, bai\feature\ResourceException;
use bai\feature\Paths, bai\feature\Files;
use bai\resource\document\Cache;


/**
 * Runtime
 * [
 *     'path' => 'path'
 *     'type' => 'type' *
 * ]
 */
class Document extends \bai\Resource
{
    use \bai\feature\CacheFileStatic;

    public const TYPE_DEFAULT = 'php';

    private static $map = [];

    protected array $runtime = [
        'configurable' => false
    ];

    protected static function setupId(array &$runtime, string $key = ''): string
    {
        $runtime['path'] = $path = static::fitPath($runtime['path'] ?? '');
        return parent::setupId($runtime, $path ?: $key);
    }

    protected static function setupEntry(array &$runtime): ?self
    {
        if (static::class !== self::class) {
            return parent::setupEntry($runtime);
        }

        $type = $runtime['type'] ?? '';
        if (!$type) {
            $filename = basename($runtime['path']);
            if ($position = strrpos($filename, '.', 1)) {
                $type = strtolower(substr($filename, $position + 1));
            } else {
                $type = static::TYPE_DEFAULT;
            }
        }
        if ($type) {
            $subClass = preg_replace_callback('/(?<=\\\\)[A-Z]/', fn($matchs) => lcfirst($matchs[0]), static::class);
            $subClass .= '\\' . ucfirst($type);
            if (class_exists($subClass)) {
                return new $subClass($runtime);
            }
        }
        return parent::setupEntry($runtime);
    }

    protected static function fitPath(string $path): ?string
    {
        if ($path) {
            if (static::TYPE_DEFAULT && !strrpos($path, '.', 1)) {
                $path .= '.' . static::TYPE_DEFAULT;
            }
            $path = str_replace('\\', '_', $path);
        }
        return $path;
    }

    protected static function preparePath(string $path, bool $fullPath = false): ?string
    {
        if ($path) {
            $path = Paths::filterPath($path);
            if (!$path) return null;

            $name = self::className(self::class);
            $root = self::config(['path', lcfirst($name), self::className()]);
            if (!$root) {
                $root = substr(static::class, strlen(self::class));
                $root = str_replace('\\', '/', $root);
                $root = (self::config(['path', $name]) ?? self::config('root') . '/resources/documents') . $root;
            }
            if (!$root) return null;

            $path = $path[0] === '/' ? "$root$path" : "$root/$path";
        }
        return $path;
    }

    protected static function checkPath(string $path, bool $autoload = true): bool
    {
        if (!self::$map && $autoload) {
            self::$map = self::loadCacheFile(self::class);
        }
        return in_array($path, self::$map);
    }

    public function run()
    {
        return file_get_contents($this->find('path'));
    }

    protected function init()
    {
        $path = static::fitPath($this->find('path') ?? '');
        $fullPath = static::preparePath($path);
        if (!$fullPath) {
            throw new RuntimeException('path', $path);
        }
        if (!static::checkPath($fullPath)) {
            throw new ResourceException(params: $path);
        }
        $this->runtime['path'] = $fullPath;
    }

    private static function refreshCacheFile(string $path)
    {
        $content = [];
        $root = '';
        $action = function (string $file, string $path, array &$result) use (&$root)
        {
            $shortPath = substr($path, strlen($root) + 1);
            $result[$root][] = $shortPath;
            $result[] = $shortPath;
            $result[] = $path;
        };

        $name = self::className(self::class);
        $root = self::config(['path', $name]);
        if ($result = Files::scanFiles($root, $action)) {
            $content = array_merge_recursive($content, $result);
        }

        $roots = self::config(['path', strtolower($name)]);
        foreach ($roots as $root) {
            if ($result = Files::scanFiles($root, $action)) {
                $content = array_merge_recursive($content, $result);
            }
        }

        if ($shortPath = Cache::fitPath($path)) {
            $content[] = $shortPath;
        }
        if ($fullPath = Cache::preparePath($shortPath)) {
            $content[] = $fullPath;
            $root = substr($fullPath, 0, - strlen($shortPath) - 1);
            $content[$root][] = $shortPath;
        }

        Cache::refresh($path, $content);
        return $content;
    }
}
